package org.ubc.cs410.disposal2020.server;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import org.ubc.cs410.disposal2020.BuildingBoundary;
import org.ubc.cs410.disposal2020.Map;
import org.ubc.cs410.disposal2020.PMF;
import org.ubc.cs410.disposal2020.client.MapService;
import org.ubc.cs410.disposal2020.client.dto.BuildingBoundaryDto;
import org.ubc.cs410.disposal2020.client.dto.MapDto;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class MapServiceImpl extends RemoteServiceServlet implements MapService {

	private static final long serialVersionUID = 1L;

	public void createMap(MapDto map) {
		insertMap2Datastore(convertMapDto2Map(map));
	}

	public static Map insertMap2Datastore(Map map) {

		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			map = pm.makePersistent(map);
		} finally {
			pm.close();
		}
		return map;
	}

	public MapDto loadMap(String name) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		MapDto mapDto;
		try {
			mapDto = convertMap2MapDto(loadMapFromDatastore(pm, name));
		} finally {
			pm.close();
		}
		return mapDto;
	}

	public MapDto loadMapById(Long id) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		MapDto mapDto;
		try {
			Map map = pm.getObjectById(Map.class, id);
			map = pm.detachCopy(map);
			mapDto = convertMap2MapDto(map);
		} finally {
			pm.close();
		}
		return mapDto;
	}

	// Helper method to allow other server side components to retrieve map
	static public Map loadMapFromDatastore(PersistenceManager pm, String name) {
		Map map = null;


		//Query q = pm.newQuery(Map.class, "name == '" + name + "'");
		//List<Map> results = (List<Map>) q.execute();

		Query query = pm.newQuery(Map.class);
		query.setFilter("name == nameParam");
		query.declareParameters("String nameParam");
		List<Map> results = (List<Map>) query.execute(name);

		if (results.size() == 1) {
			map = results.get(0);
			map = pm.detachCopy(map);
		}


		return map;
	}

	// Helper method to allow other server side components to retrieve map
	static public Map loadMapFromDatastoreNoDetach(PersistenceManager pm, String name) {
		Map map = null;

		//Query q = pm.newQuery(Map.class, "name == '" + name + "'");
		//List<Map> results = (List<Map>) q.execute();

		Query q = pm.newQuery(Map.class);
		q.setFilter("name = nameParam");
		q.declareParameters("String nameParam");
		List<Map> results = (List<Map>) q.execute(name);

		if (results.size() == 1) {
			map = results.get(0);
		}

		return map;
	}

	@Override
	public MapDto deleteMap(String name) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		MapDto mapDto = null;
		try {
			Map delMap = loadMapFromDatastoreNoDetach(pm, name);
			Map map = loadMapFromDatastore(pm, name);
			mapDto = convertMap2MapDto(map);
			pm.deletePersistent(delMap);
		} finally {
			pm.close();
		}
		return mapDto;
	}

	@Override
	public Collection<String> getAllMapNames() {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Collection<String> returnList = new ArrayList<String>();
		try {
			Query query = pm.newQuery(Map.class);
			List<Map> results = (List<Map>) query.execute();
			for (Map map : results) {
				returnList.add(map.getName());
			}
		} finally {
			pm.close();
		}
		return returnList;
	}

	@Override
	public void updateMap(MapDto mapDto) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			Map map = loadMapFromDatastore(pm, mapDto.getName());

			map.setDescription(mapDto.getDescription());
			map.setCenterLatitude(mapDto.getCenterLatitude());
			map.setCenterLongitude(mapDto.getCenterLongitude());
			map.setTotalGarbage(mapDto.getTotalGarbage());
			map.setZoomLevel(mapDto.getZoomLevel());

			Set<BuildingBoundary> buildingBoundaryStore = new HashSet<BuildingBoundary>();

			for (BuildingBoundaryDto buildingBoundary : mapDto
					.getBuildingList()) {
				BuildingBoundary newStore = new BuildingBoundary(
						buildingBoundary.getBuildingId(), buildingBoundary
						.getLatitudes(), buildingBoundary
						.getLongitudes());
				buildingBoundaryStore.add(newStore);
			}

			map.setBuildings(buildingBoundaryStore);
			pm.makePersistent(map);
		} finally {
			pm.close();
		}
	}

	/**
	 * A help method to convert MapDto information into Map format.
	 * @param mapDto
	 * @return
	 */
	public static Map convertMapDto2Map(MapDto mapDto) {
		Set<BuildingBoundary> buildingBoundaryStore = new HashSet<BuildingBoundary>();

		for (BuildingBoundaryDto buildingBoundary : mapDto.getBuildingList()) {
			BuildingBoundary newStore = new BuildingBoundary(buildingBoundary
					.getBuildingId(), buildingBoundary.getLatitudes(),
					buildingBoundary.getLongitudes());
			buildingBoundaryStore.add(newStore);
		}

		Map mapStore = new Map(mapDto.getName(), mapDto.getDescription(),
				mapDto.getTotalGarbage(), mapDto.getCenterLatitude(), mapDto
				.getCenterLongitude(), mapDto.getZoomLevel(),
				buildingBoundaryStore);
		return mapStore;
	}

	/**
	 * A helper method to covert Map (datastore obj) into MapDto (client obj)
	 * @param map
	 * @return
	 */
	public static MapDto convertMap2MapDto(Map map) {
		MapDto mapDto = null;
		if (map != null) {
			List<BuildingBoundaryDto> buildingBoundaryList = new ArrayList<BuildingBoundaryDto>();

			for (BuildingBoundary buildingBoundaryStore : map.getBuildings()) {
				BuildingBoundaryDto buildingBoundary = new BuildingBoundaryDto(
						buildingBoundaryStore.getBuildingId(),
						buildingBoundaryStore.getLatitudes(),
						buildingBoundaryStore.getLongitudes());
				buildingBoundaryList.add(buildingBoundary);
			}

			mapDto = new MapDto(map.getName(), map.getDescription(), map
					.getTotalGarbage(), map.getCenterLatitude(), map
					.getCenterLongitude(), map.getZoomLevel(),
					buildingBoundaryList);
		}
		return mapDto;
	}

}